#include "NDF-ShaderStructs.fxh"
#include "Lighting.fxh"

//Constants

#define MAX_BONE_COUNT 72

//Global variables

float4x4 World;
float4x4 View;
float4x4 Projection;


Texture Texture0;

sampler texture0 = sampler_state
	 { texture = <Texture0>; magfilter = LINEAR; minfilter = LINEAR; mipfilter = LINEAR; AddressU = wrap; AddressV = wrap; };

float4x3 BoneTransforms[MAX_BONE_COUNT];

//Colored, no shading

ColoredNoShadingVSOutput ColoredNoShadingVertexShader(ColoredNoShadingVSInput input)
{
	float4x4 wvp = mul(World, mul(View, Projection));

	ColoredNoShadingVSOutput output;

	output.Position = mul(input.Position, wvp);
	output.Color = input.Color;

	return output;
}

float4 ColoredNoShadingPixelShader(ColoredNoShadingPSInput input) : COLOR0
{
	return input.Color;
}

Technique ColoredNoShading
{
	pass Pass0
	{
		  VertexShader = compile vs_2_0 ColoredNoShadingVertexShader();
		  PixelShader = compile ps_2_0 ColoredNoShadingPixelShader();
	}
}

//Hard-coded Colored, Shaded

ColoredShadedVSOutput ColoredShadedVertexShader(ColoredShadedVSInput input)
{
	float4x4 wvp = mul(World, mul(View, Projection));

	ColoredShadedVSOutput output;

	output.Position = mul(input.Position, wvp);
	output.Normal = mul(input.Normal, (float3x3) World);
	float4 worldPos = mul(input.Position, World);
	output.WorldPosition = worldPos / worldPos.w;

	return output;
}

float4 ColoredShadedPixelShader(ColoredShadedPSInput input) : COLOR0
{
	return float4(1.0f, 0, 0, 0) * ComputeLighting(input.Normal, input.WorldPosition);
}

Technique ColoredShaded
{
	pass Pass0
	{
		  VertexShader = compile vs_2_0 ColoredShadedVertexShader();
		  PixelShader = compile ps_2_0 ColoredShadedPixelShader();
	}
}

//Textured, Shaded

TexturedVSOutput TexturedVertexShader(TexturedVSInput input)
{
	float4x4 wvp = mul(World, mul(View, Projection));

	TexturedVSOutput output;

	output.Position = mul(input.Position, wvp);
	output.Normal = mul(input.Normal, (float3x3) World);
	float4 worldPos = mul(input.Position, World);
	output.WorldPosition = worldPos / worldPos.w;
	output.TextureCoords = input.TextureCoords;

	return output;
}

float4 TexturedPixelShader(TexturedPSInput input) : COLOR0
{
	float4 globalLighting = ComputeLighting(input.Normal, input.WorldPosition);
	float4 textureColor = tex2D(texture0, input.TextureCoords);

	float4 finalColor = textureColor * globalLighting;
	finalColor.a = 1.0f;

	return finalColor;
};

Technique Textured
{
	pass Pass0
	{
		  VertexShader = compile vs_2_0 TexturedVertexShader();
		  PixelShader = compile ps_2_0 TexturedPixelShader();
	}
}

//Skinned

void Skin(inout SkinnedVSInput input, half boneCount)
{
	float4x3 boneTransforms = 0;
	
	for(half i = 0; i < boneCount; i++)
	{
		boneTransforms += BoneTransforms[input.BoneIndices[i]] * input.BoneWeights[i];
	}
	
	input.Position.xyz = mul(input.Position, boneTransforms);
	input.Normal = mul(input.Normal, (float3x3) boneTransforms);
}

void Skin(inout SkinnedShadowMapVSInput input, half boneCount)
{
	float4x3 boneTransforms = 0;
	
	for(half i = 0; i < boneCount; i++)
	{
		boneTransforms += BoneTransforms[input.BoneIndices[i]] * input.BoneWeights[i];
	}
	
	input.Position.xyz = mul(input.Position, boneTransforms);
}

SkinnedVSOutput SkinnedVertexShader(SkinnedVSInput input)
{
	float4x4 wvp = mul(mul(World, View), Projection);

	SkinnedVSOutput output;
	Skin(input, 4);

	output.Position = mul(input.Position, wvp);
	output.Normal = mul(input.Normal, (float3x3)World);
	output.TextureCoords = input.TextureCoords;
	float4 worldPos = mul(input.Position, World);
	output.WorldPosition = worldPos / worldPos.w;

	return output;
}

float4 SkinnedPixelShader(SkinnedPSInput input) : COLOR0
{
	float4 globalLighting = ComputeLighting(input.Normal, input.WorldPosition);
	float4 textureColor = tex2D(texture0, input.TextureCoords);

	float4 finalColor = textureColor * globalLighting;
	finalColor.a = 1.0f;

	return finalColor;
}

Technique Skinned
{
	pass Pass0
	{
		VertexShader = compile vs_2_0 SkinnedVertexShader();
		PixelShader = compile ps_2_0 SkinnedPixelShader();
	}
}

//Shadow Map

ShadowMapVSOutput ShadowMapVertexShader(float4 inputPosition : POSITION0)
{
	ShadowMapVSOutput output;

	float4x4 lightWVP = mul(World, LightViewProjection);

	output.Position = mul(inputPosition, lightWVP);
	output.Position2D = output.Position;

	return output;
}

float4 ShadowMapPixelShader(float4 inputPosition : TEXCOORD0) : COLOR0
{
	float4 depthColor = inputPosition.z / inputPosition.w;
	
	return pow(depthColor, 64);
}

Technique ShadowMap
{
	pass Pass0
	{
		VertexShader = compile vs_2_0 ShadowMapVertexShader();
		PixelShader = compile ps_2_0 ShadowMapPixelShader();
	}
}

//Skinned shadow map
SkinnedShadowMapVSOutput SkinnedShadowMapVertexShader(SkinnedShadowMapVSInput input)
{
	SkinnedShadowMapVSOutput output;

	float4x4 lightWVP = mul(World, LightViewProjection);

	Skin(input, 4);

	output.Position = mul(input.Position, lightWVP);
	output.Position2D = output.Position;

	return output;
}

float4 SkinnedShadowMapPixelShader(float4 inputPosition : TEXCOORD0) : COLOR0
{
	float4 depthColor = inputPosition.z / inputPosition.w;
	
	return pow(depthColor, 64);
}

Technique SkinnedShadowMap
{
	pass Pass0
	{
		VertexShader = compile vs_2_0 SkinnedShadowMapVertexShader();
		PixelShader = compile ps_2_0 SkinnedShadowMapPixelShader();
	}
}

//Textured with shadows

TexturedWithShadowsVSOutput TexturedWithShadowsVertexShader(TexturedWithShadowsVSInput input)
{
	float4x4 wvp = mul(mul(World, View), Projection);
	float4x4 lightWVP = mul(World, LightViewProjection);

	TexturedWithShadowsVSOutput output;

	output.Position = mul(input.Position, wvp);
	output.PositionAsSeenByLight = mul(input.Position, lightWVP);
	output.WorldPosition = mul(input.Position, World);
	output.Normal = mul(input.Normal, World);
	output.TextureCoordinates = input.TextureCoordinates;

	return output;
}

float4 TexturedWithShadowsPixelShader(TexturedWithShadowsPSInput input) : COLOR0
{
	float4 lightColor = ComputeLighting(input.Normal, input.WorldPosition);

	float2 ProjectedShadowCoordinates = { input.PositionAsSeenByLight.x, input.PositionAsSeenByLight.y };
	ProjectedShadowCoordinates = ProjectedShadowCoordinates / input.PositionAsSeenByLight.w;
	ProjectedShadowCoordinates.x = ProjectedShadowCoordinates.x / 2.0f + 0.5;
	ProjectedShadowCoordinates.y = -ProjectedShadowCoordinates.y / 2.0f + 0.5;

	
	{
		float depthStoredInShadowMap = tex2D(shadowMapSampler, ProjectedShadowCoordinates).r;
		float realDistance = input.PositionAsSeenByLight.z / input.PositionAsSeenByLight.w;

		if (realDistance - 1.0f/1000.0f > depthStoredInShadowMap) 
		{
			lightColor *= float4(0.5f, 0.5f, 0.5f, 0.5f);
		}
	}

	float4 baseColor = tex2D(texture0, input.TextureCoordinates);
	float4 finalColor = baseColor * lightColor;

	return finalColor;
}

Technique TexturedWithShadows
{
	pass Pass0
	{
		VertexShader = compile vs_2_0 TexturedWithShadowsVertexShader();
		PixelShader = compile ps_2_0 TexturedWithShadowsPixelShader();
	}
}